﻿/*
 * GridFrameExtent
 * 
 * initial implementation : 8.12.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Windows;
using System.Collections.Generic;
using Petra.Gis.Geometry.Support;
using Petra.Gis.Geometry.GridSupport;

namespace Petra.Gis.Geometry.Utils
{

  /// <summary>
  /// Grid data plane extent in world (projected or geogr.) coordinate system
  /// and its mapping to local grid cartesian graph (horizontal x vertical grid resolution)
  /// Extent is given by <c>BoundingBox</c> that is rounded to start/end
  /// of whole grid cell (pixel) position and number of pixels
  /// in horizontal and vertical direction.
  /// Extent can also represent single rectangular subset (area of interest)
  /// of whole Grid extent given by <c>BoundingBox</c> and start and end cell
  /// position. Cell position is given by <c>Int32Rect</c>.
  /// </summary>
  public class GridFrameExtent
  {


    protected GridFrameExtent(GridFrameBase grFrame, BoundingBox aoiBounds)
    {

      // update aoi first to tight to frame bounds
      // aoi is usually map range, transformed into
      // grid data coord system
      if (aoiBounds.Left < grFrame.Bounds.Left)
        aoiBounds.Left = grFrame.Bounds.Left;

      if (aoiBounds.Bottom < grFrame.Bounds.Bottom)
        aoiBounds.Bottom = grFrame.Bounds.Bottom;

      if (aoiBounds.Right > grFrame.Bounds.Right)
        aoiBounds.Right = grFrame.Bounds.Right;

      if (aoiBounds.Top > grFrame.Bounds.Top)
        aoiBounds.Top = grFrame.Bounds.Top;

      double pixelWidth = grFrame.Bounds.Width / (grFrame.Width *  1.0);
      double pixelHeight = grFrame.Bounds.Height / (grFrame.Height * 1.0);

      //Int32Rect _gridPlaneBounds = new Int32Rect();
      _gridPlaneBounds = new Int32Rect();
      _gridPlaneBounds.X = (int)((aoiBounds.Left - grFrame.Bounds.Left) / pixelWidth);
      _gridPlaneBounds.Y = grFrame.Height - (int)((aoiBounds.Top - grFrame.Bounds.Bottom) / pixelHeight);

      _gridPlaneBounds.Width = (int)((aoiBounds.Right - grFrame.Bounds.Left) / pixelWidth) - _gridPlaneBounds.X;
      _gridPlaneBounds.Height = (grFrame.Height - (int)((aoiBounds.Bottom - grFrame.Bounds.Bottom) / pixelHeight)) - _gridPlaneBounds.Y;

      // prepare world bounds
      //BoundingBox _bounds = new BoundingBox();
      _bounds = new BoundingBox();
      _bounds.Left = grFrame.Bounds.Left + (_gridPlaneBounds.X * pixelWidth);
      _bounds.Right = grFrame.Bounds.Left + ((_gridPlaneBounds.X + _gridPlaneBounds.Width) * pixelWidth);
      _bounds.Top = grFrame.Bounds.Bottom + ((grFrame.Height - _gridPlaneBounds.Y) * pixelHeight);
      _bounds.Bottom = grFrame.Bounds.Bottom + ((grFrame.Height - (_gridPlaneBounds.Y + _gridPlaneBounds.Height)) * pixelHeight);
   
    }




    protected BoundingBox _bounds;

    /// <summary>
    /// Spatial extent in world (projected or geographic) coordinate system
    /// of this grid frame extent.
    /// </summary>
    public BoundingBox Bounds
    {
      get { return _bounds; }
      //set { _bounds = value; }
    }





    protected Int32Rect _gridPlaneBounds;

    /// <summary>
    /// returns size (number of cells) in vertical and horizontal direction
    /// and position of starting cell of current grid frame extent.
    /// This is in fact position in local cartesian coordinate system of grid
    /// </summary>
    public Int32Rect GridPlaneBounds
    {
      get { return _gridPlaneBounds; }
      //set { _gridPlaneBounds = value; }
    }




    /// <summary>
    /// Creates subset of grid data plane, defined by input extent (area of interest) <c>BoundingBox</c>.
    /// Input <c>BoundingBox</c> is rounded to start/end of whole grid cell (pixel) position.
    /// Horizontal and vertical dimension and spatial position of subset, is computed automatically.
    /// When no cell (pixel) falls into this subset area, null is returned, Othewise new grid frame extent is returned
    /// </summary>
    /// <param name="grFrame">given grid frame</param>
    /// <param name="aoiBounds">user defined area of interest to select pixels from</param>
    /// <returns>valid extent or null when area of interest is totally outside of range of this grid</returns>
    public static GridFrameExtent Create(GridFrameBase grFrame, BoundingBox aoiBounds)
    {
      // if not aoiBounds overlaps GridFrame.Bounds
      // do nothing, return null
      if (!(
        grFrame.Bounds.Left < (aoiBounds.Right) &&
        grFrame.Bounds.Bottom < (aoiBounds.Top) &&
        aoiBounds.Left < grFrame.Bounds.Right &&
        aoiBounds.Bottom < grFrame.Bounds.Top))
        return null;
      else
      {
        // if overlaps try create gfe
        GridFrameExtent result = new GridFrameExtent(grFrame, aoiBounds);
        // but if gfe dimension is smaller than 1 px in any direction
        // return null also
        if (result.GridPlaneBounds.Height > 0 && result.GridPlaneBounds.Width > 0)
          return result;
        else
          return null;
      }

    }

  }
}
